package com.cultraview;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;

import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import com.cultraview.SummaryRectsInfo.SummaryType;

public class IRFinderDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// Load the native OpenCV library
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

		new userInterface().initFrame();
	}

}

class userInterface{
	private static final String WINDOW_NAME = "IR Finder Demo";
	private JFrame frame;
	// control panel
	private JPanel controlPanel;
	private JButton resetButton;
	private JCheckBox grayScaleCheckBox;
	private JCheckBox blurCheckBox;
	private JCheckBox erodeCheckBox;
	private JCheckBox dilateCheckBox;
	private JCheckBox findContoursCheckBox;
	private JCheckBox contoursFilterCheckBox;
	private JCheckBox findIrPanelCheckBox;
	// content panel
	private JPanel contentPanel;
	private JLabel srcImageLable;
	private MouseLable dstImageLable;
	private MouseLable.MouseOperateCallBack callback;

	// data
	private Mat imgSrcMat = new Mat();
	private Mat imgDstMat = new Mat();
	private int blurKernelSize = 2;
	private int erodeKernelSize = 2;
	private int erodeType = Imgproc.CV_SHAPE_ELLIPSE;
	private int dilateKernelSize = 3;
	private int dilateType = Imgproc.CV_SHAPE_ELLIPSE;// Imgproc.CV_SHAPE_RECT;//Imgproc.CV_SHAPE_CROSS;//Imgproc.CV_SHAPE_ELLIPSE;
	private final int findContoursThreshold = 100;
	private final int filterContoursTolerance = 10;
	// private Rect[] contoursRect;
	private ArrayList<Rect> contoursRects = new ArrayList<>();
	private ArrayList<SummaryRectsInfo> summaryInfos = new ArrayList<>();
	private PanelRect irPanel1, irPanel2;

	public void initFrame() {
		// Create and set up the window.
		frame = new JFrame(WINDOW_NAME);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		// Set up the content pane.
		setupControlPane(frame.getContentPane());
		setupContentPane(frame.getContentPane());

		// Use the content pane's default BorderLayout. No need for
		// setLayout(new BorderLayout());
		// Display the window.
		frame.setPreferredSize(new Dimension(800, 600));
		frame.pack();
		frame.setVisible(true);

	}

	private void setupContentPane(Container pane) {
		// TODO Auto-generated method stub
		contentPanel = new JPanel();
		contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.X_AXIS));
		imgSrcMat = Imgcodecs.imread("./data/499999.png");
		imgDstMat = imgSrcMat.clone();
		Image img = HighGui.toBufferedImage(imgSrcMat);
		srcImageLable = new JLabel(new ImageIcon(img));
		srcImageLable.setPreferredSize(new Dimension(320, 600));
		dstImageLable = new MouseLable(new ImageIcon(img));
		srcImageLable.setPreferredSize(new Dimension(320, 600));

		callback = new MouseLable.MouseOperateCallBack() {

			@Override
			public void removeRect(int x, int y) {
				// TODO Auto-generated method stub
				if(irPanel1 != null && irPanel2 != null){
					do{
						Point p = new Point(x, y);
						if(irPanel1.getChildCount()>0) {
							ArrayList<Rect> rects = irPanel1.getChilds();
							for (int i = 0; i < rects.size(); i++) {
								if(rects.get(i).contains(p)){
									System.out.println("irpanel1 remove rect " + rects.get(i).toString());
									rects.remove(i);
									break;
								}
							}
						}

						if(irPanel2.getChildCount()>0) {
							ArrayList<Rect> rects = irPanel2.getChilds();
							for (int i = 0; i < rects.size(); i++) {
								if(rects.get(i).contains(p)){
									System.out.println("irpanel2 remove rect " + rects.get(i).toString());
									rects.remove(i);
									break;
								}
							}
						}

						break;
					}while(true);
					drawPanelRect();
				}
			}

			@Override
			public void addRect(int x, int y, int width, int hight) {
				// TODO Auto-generated method stub
				if(irPanel1 != null && irPanel2 != null) {
					do {
						Rect rect;
						if(width !=0 && hight !=0) {
							rect = new Rect(x, y, width, hight);
						} else {
							rect = regionGrowth(x, y, imgDstMat);
						}
						System.out.println("rect " + rect.toString());
						if(irPanel1.getRect().contains(rect.tl()) && irPanel1.getRect().contains(rect.br())){
							irPanel1.addChild(rect);
							System.out.println("irPanel1 add rect " + rect.toString());
							break;
						}
						if(irPanel2.getRect().contains(rect.tl()) && irPanel2.getRect().contains(rect.br())){
							irPanel2.addChild(rect);
							System.out.println("irPanel2 add rect " + rect.toString());
							break;
						}
						break;
					}while(true);
					drawPanelRect();
				}
			}

		};
		dstImageLable.setCallBack(callback);
		
		contentPanel.add(srcImageLable);
		contentPanel.add(dstImageLable);
		pane.add(new JScrollPane(contentPanel), BorderLayout.CENTER);
	}

	private void setupControlPane(Container pane) {
		// TODO Auto-generated method stub
		controlPanel = new JPanel();
		controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
		// coordinate
		resetButton = new JButton("Reset");
		resetButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				imgDstMat = imgSrcMat.clone();
				Image img = HighGui.toBufferedImage(imgDstMat);
				dstImageLable.setIcon(new ImageIcon(img));

				grayScaleCheckBox.setEnabled(true);
				grayScaleCheckBox.setSelected(false);
				blurCheckBox.setEnabled(true);
				blurCheckBox.setSelected(false);
				erodeCheckBox.setEnabled(true);
				erodeCheckBox.setSelected(false);
				dilateCheckBox.setEnabled(true);
				dilateCheckBox.setSelected(false);
				findContoursCheckBox.setEnabled(true);
				findContoursCheckBox.setSelected(false);
				contoursFilterCheckBox.setEnabled(true);
				contoursFilterCheckBox.setSelected(false);
				findIrPanelCheckBox.setEnabled(true);
				findIrPanelCheckBox.setSelected(false);
			}
		});
		controlPanel.add(resetButton);

		// gray
		grayScaleCheckBox = new JCheckBox("Grayscale");
		grayScaleCheckBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				JCheckBox cb = (JCheckBox) arg0.getSource();
				if (cb.isSelected()) {
					Imgproc.cvtColor(imgDstMat, imgDstMat, Imgproc.COLOR_BGR2GRAY);
					Image img = HighGui.toBufferedImage(imgDstMat);
					dstImageLable.setIcon(new ImageIcon(img));
					grayScaleCheckBox.setEnabled(false);

				} else {

				}
			}
		});
		controlPanel.add(grayScaleCheckBox);

		blurCheckBox = new JCheckBox("Blur");
		blurCheckBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				JCheckBox cb = (JCheckBox) arg0.getSource();
				if (cb.isSelected()) {
					Imgproc.blur(imgDstMat, imgDstMat, new Size(blurKernelSize, blurKernelSize));
					Image img = HighGui.toBufferedImage(imgDstMat);
					dstImageLable.setIcon(new ImageIcon(img));
					blurCheckBox.setEnabled(false);
				} else {

				}
			}
		});
		controlPanel.add(blurCheckBox);

		erodeCheckBox = new JCheckBox("Erode");
		erodeCheckBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				JCheckBox cb = (JCheckBox) arg0.getSource();
				if (cb.isSelected()) {
					Mat element = Imgproc.getStructuringElement(erodeType,
							new Size(2 * erodeKernelSize + 1, 2 * erodeKernelSize + 1),
							new Point(erodeKernelSize, erodeKernelSize));
					Imgproc.erode(imgDstMat, imgDstMat, element);
					Image img = HighGui.toBufferedImage(imgDstMat);
					dstImageLable.setIcon(new ImageIcon(img));
					erodeCheckBox.setEnabled(false);
				} else {

				}
			}
		});
		controlPanel.add(erodeCheckBox);

		dilateCheckBox = new JCheckBox("Dilate");
		dilateCheckBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				JCheckBox cb = (JCheckBox) arg0.getSource();
				if (cb.isSelected()) {
					Mat element = Imgproc.getStructuringElement(dilateType,
							new Size(2 * dilateKernelSize + 1, 2 * dilateKernelSize + 1),
							new Point(dilateKernelSize, dilateKernelSize));
					Imgproc.dilate(imgDstMat, imgDstMat, element);
					Image img = HighGui.toBufferedImage(imgDstMat);
					dstImageLable.setIcon(new ImageIcon(img));
					dilateCheckBox.setEnabled(false);
				} else {

				}
			}
		});
		controlPanel.add(dilateCheckBox);

		findContoursCheckBox = new JCheckBox("Find Contours");
		findContoursCheckBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				JCheckBox cb = (JCheckBox) arg0.getSource();
				if (cb.isSelected()) {
					contoursRects = findContours(imgDstMat, findContoursThreshold);
					for (int i = 0; i < contoursRects.size(); i++) {
						System.out.println("contoursRect[" + i + "]" + ":" + contoursRects.get(i).toString());
						Scalar color = new Scalar(0, 0, 255);
						Imgproc.rectangle(imgDstMat, contoursRects.get(i).tl(), contoursRects.get(i).br(), color, 1);
					}
					Image img = HighGui.toBufferedImage(imgDstMat);
					dstImageLable.setIcon(new ImageIcon(img));
					findContoursCheckBox.setEnabled(false);
				} else {

				}
			}
		});
		controlPanel.add(findContoursCheckBox);

		contoursFilterCheckBox = new JCheckBox("Contours Filter");
		contoursFilterCheckBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				JCheckBox cb = (JCheckBox) arg0.getSource();
				if (cb.isSelected()) {
					contoursRects = contoursFilter(contoursRects, filterContoursTolerance);
					for (int i = 0; i < contoursRects.size(); i++) {
						// System.out.println("afterFilterRect[" + i + "]" + ":"
						// + contoursRects.get(i).toString());
						Scalar color = new Scalar(0, 0, 255);
						Imgproc.rectangle(imgDstMat, contoursRects.get(i).tl(), contoursRects.get(i).br(), color, 1);
					}
					Image img = HighGui.toBufferedImage(imgDstMat);
					dstImageLable.setIcon(new ImageIcon(img));
					contoursFilterCheckBox.setEnabled(false);
				} else {

				}
			}
		});
		controlPanel.add(contoursFilterCheckBox);

		findIrPanelCheckBox = new JCheckBox("Find Ir Panel");
		findIrPanelCheckBox.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				JCheckBox cb = (JCheckBox) arg0.getSource();
				if (cb.isSelected()) {
					ArrayList<PanelRect> panelrects = new ArrayList<>();
					findIrPanel();
					findIrPanelCheckBox.setEnabled(false);
				} else {

				}
			}
		});
		controlPanel.add(findIrPanelCheckBox);

		controlPanel.setPreferredSize(new Dimension(150, 40));
		controlPanel.setBackground(Color.GRAY);
		pane.add(controlPanel, BorderLayout.LINE_START);
	}

	private Rect regionGrowth(int seedx, int seedy, Mat img){
		int tolerance = 10;
		int channels = img.channels();
		byte[] bytes =new byte[channels];
		img.get(seedy, seedx, bytes);
		System.out.println("{" + seedx + ", " + seedy + "} [BGR]:" + (bytes[0]&0xff) + "," + (bytes[1]&0xff) + "," + (bytes[2]&0xff));
		
		int top=seedy, down=seedy, left=seedx, right=seedx;
		ArrayList<Point> growthPoints = new ArrayList<>();
		Point seed = new Point(seedx, seedy);
		growthPoints.add(seed);
		boolean beinggrowth;
		Point p;
		for (int i = 0; ; i++) {
			//new point
			int x = (int) growthPoints.get(i).x;
			int y = (int) growthPoints.get(i).y;
			//up
			beinggrowth = true;
			byte[] upBytes = new byte[channels];
			img.get(y-1, x, upBytes);
			for (int j = 0; j < upBytes.length; j++) {
				if(Math.abs((bytes[j]&0xff) - (upBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x, y-1);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					top = Math.min(top, y-1);
					System.out.println("Growth Point " + p.toString());
				}
			}
			
			//down
			beinggrowth = true;
			byte[] downBytes = new byte[channels];
			img.get(y+1, x, downBytes);
			for (int j = 0; j < downBytes.length; j++) {
				if(Math.abs((bytes[j]&0xff) - (downBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x, y+1);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					down = Math.max(down, y+1);
					System.out.println("Growth Point " + p.toString());
				}
			}
			
			//left
			beinggrowth = true;
			byte[] leftBytes = new byte[channels];
			img.get(y, x-1, leftBytes);
			for (int j = 0; j < leftBytes.length; j++) {
				if(Math.abs((bytes[j]&0xff) - (leftBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x-1, y);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					left = Math.min(left, x-1);
					System.out.println("Growth Point " + p.toString());
				}
			}
			//right
			beinggrowth = true;
			byte[] rightBytes = new byte[channels];
			img.get(y, x+1, rightBytes);
			for (int j = 0; j < rightBytes.length; j++) {
				if(Math.abs((bytes[j]&0xff) - (rightBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x+1, y);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					right = Math.max(right, x+1);
					System.out.println("Growth Point " + p.toString());
				}
			}
			
			System.out.println("Points size " + growthPoints.size() + " i " + i);
			if(i >= growthPoints.size() -1) break;
		}
		return new Rect(new Point(left-1, top-1), new Point(right+1,down+1));
	}
	
	private Rect regionGrowthHsv(int seedx, int seedy, Mat img){
		int tolerance = 10;
		final int MAX_HUE = 180;
		final int MAX_SAT = 255;
		final int MAX_VAL = 255;
		Mat hsv_image = new Mat(img.size(), img.type());
		Imgproc.cvtColor(img, hsv_image, Imgproc.COLOR_BGR2HSV);
		
		int channels = hsv_image.channels();
		byte[] bytes =new byte[channels];
		hsv_image.get(seedy, seedx, bytes);
		boolean cycle_cal_hue = ((bytes[0]&0xff) + tolerance >MAX_HUE || (bytes[0]&0xff) - tolerance < 0);
		System.out.println("cycle_cal_hue:"+ cycle_cal_hue+" {" + seedx + ", " + seedy + "} [HSV]:" + (bytes[0]&0xff) + "," + (bytes[1]&0xff) + "," + (bytes[2]&0xff));
		
		int top=seedy, down=seedy, left=seedx, right=seedx;
		ArrayList<Point> growthPoints = new ArrayList<>();
		Point seed = new Point(seedx, seedy);
		growthPoints.add(seed);
		boolean beinggrowth;
		Point p;
		for (int i = 0; ; i++) {
			//new point
			int x = (int) growthPoints.get(i).x;
			int y = (int) growthPoints.get(i).y;
			//up
			beinggrowth = true;
			byte[] upBytes = new byte[channels];
			hsv_image.get(y-1, x, upBytes);
			for (int j = 0; j < upBytes.length; j++) {
				if(cycle_cal_hue && j ==0) {
					if(Math.abs(Math.abs((bytes[j]&0xff) - (upBytes[j]&0xff) -MAX_HUE)) > tolerance){
						beinggrowth = false;
						break;
					}
				}else if(Math.abs((bytes[j]&0xff) - (upBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x, y-1);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					top = Math.min(top, y-1);
					//System.out.println("Growth Point " + p.toString());
				}
			}
			
			//down
			beinggrowth = true;
			byte[] downBytes = new byte[channels];
			hsv_image.get(y+1, x, downBytes);
			System.out.println("{" +x + "," + y +"}" + " H("+(bytes[0]&0xff)+"-"+(downBytes[0]&0xff)+")" + " S("+(bytes[1]&0xff)+"-"+(downBytes[1]&0xff)+")" + " V("+(bytes[2]&0xff)+"-"+(downBytes[2]&0xff)+")");
			for (int j = 0; j < downBytes.length; j++) {
				if(cycle_cal_hue && j ==0) {
					if(Math.abs(Math.abs((bytes[j]&0xff) - (downBytes[j]&0xff) -MAX_HUE)) > tolerance){
						beinggrowth = false;
						break;
					}
				}else if(Math.abs((bytes[j]&0xff) - (downBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x, y+1);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					down = Math.max(down, y+1);
					System.out.println("Growth Point " + p.toString());
				}
			}
			
			//left
			beinggrowth = true;
			byte[] leftBytes = new byte[channels];
			hsv_image.get(y, x-1, leftBytes);
			for (int j = 0; j < leftBytes.length; j++) {
				if(cycle_cal_hue && j ==0) {
					if(Math.abs(Math.abs((bytes[j]&0xff) - (leftBytes[j]&0xff) -MAX_HUE)) > tolerance){
						beinggrowth = false;
						break;
					}
				}else if(Math.abs((bytes[j]&0xff) - (leftBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x-1, y);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					left = Math.min(left, x-1);
					//System.out.println("Growth Point " + p.toString());
				}
			}
			//right
			beinggrowth = true;
			byte[] rightBytes = new byte[channels];
			hsv_image.get(y, x+1, rightBytes);
			for (int j = 0; j < rightBytes.length; j++) {
				if(cycle_cal_hue && j ==0) {
					if(Math.abs(Math.abs((bytes[j]&0xff) - (rightBytes[j]&0xff) -MAX_HUE)) > tolerance){
						beinggrowth = false;
						break;
					}
				}else if(Math.abs((bytes[j]&0xff) - (rightBytes[j]&0xff)) > tolerance){
					beinggrowth = false;
					break;
				}
			}
			if(beinggrowth){
				p = new Point(x+1, y);
				if(! growthPoints.contains(p)){
					growthPoints.add(p);
					right = Math.max(right, x+1);
					//System.out.println("Growth Point " + p.toString());
				}
			}
			
			//System.out.println("Points size " + growthPoints.size() + " i " + i);
			if(i >= growthPoints.size() -1) break;
		}
		return new Rect(new Point(left-1, top-1), new Point(right+1,down+1));
	}
	private ArrayList<Rect> findContours(Mat imgMat, int threshold) {

		// ! [Canny]
		// / Detect edges using Canny
		Mat cannyOutput = new Mat();
		Imgproc.Canny(imgMat, cannyOutput, threshold, threshold * 2);
		// ! [Canny]

		// ! [findContours]
		// / Find contours
		// RETR_TREE����һ���ȼ����ṹ������,RETR_EXTERNAL��ʾֻ���������,RETR_LIST���������������ȼ���ϵ,RETR_CCOMP���������ȼ��������������һ��Ϊ��߽磬�����һ��Ϊ�ڿ׵ı߽���Ϣ������ڿ��ڻ���һ����ͨ���壬�������ı߽�Ҳ�ڶ���
		
		List<MatOfPoint> contours = new ArrayList<>();
		Mat hierarchy = new Mat();
		Imgproc.findContours(cannyOutput, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

		// ! [findContours]

		// ! [allthework]
		// / Approximate contours to polygons + get bounding rects and circles
		MatOfPoint2f[] contoursPoly = new MatOfPoint2f[contours.size()];
		ArrayList<Rect> boundRects = new ArrayList<>();
		// Point[] centers = new Point[contours.size()];
		// float[][] radius = new float[contours.size()][1];

		for (int i = 0; i < contours.size(); i++) {
			// if (i>100) break;
			contoursPoly[i] = new MatOfPoint2f();
			Imgproc.approxPolyDP(new MatOfPoint2f(contours.get(i).toArray()), contoursPoly[i], 3, true);
			boundRects.add(Imgproc.boundingRect(new MatOfPoint(contoursPoly[i].toArray())));
			// rect = Imgproc.boundingRect(new
			// MatOfPoint(contoursPoly[i].toArray()));

			// centers[i] = new Point();
			// Imgproc.minEnclosingCircle(contoursPoly[i], centers[i],
			// radius[i]);
		}
		// ! [allthework]

		// ! [zeroMat]
		// Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);
		// ! [zeroMat]
		// ! [forContour]
		// / Draw polygonal contour + bonding rects + circles
		// List<MatOfPoint> contoursPolyList = new
		// ArrayList<>(contoursPoly.length);
		// for (MatOfPoint2f poly : contoursPoly) {
		// contoursPolyList.add(new MatOfPoint(poly.toArray()));
		// }

		// for (int i = 0; i < contours.size(); i++) {
		// Scalar color = new Scalar(0, 0, 255);
		// Imgproc.drawContours(dstMat, contoursPolyList, i, color);
		// Imgproc.rectangle(dstMat, boundRect[i].tl(), boundRect[i].br(),
		// color, 1);
		// System.out.println("boundRect[" + i + "]:" +
		// boundRect[i].toString());
		// Imgproc.circle(drawing, centers[i], (int) radius[i][0], color, 2);
		// }
		// ! [forContour]

		return boundRects;
	}

	private ArrayList<Rect> contoursFilter(ArrayList<Rect> findrects, int tolerance) {
		Rect rect, rect2;
		ArrayList<Rect> rects = findrects;
		for (int i = 0; i < rects.size(); i++) {
			// System.out.println("\n## rect[" + i + "]");
			rect = rects.get(i);
			// find similar rect, and rest height width
			for (int j = i + 1; j < rects.size(); j++) {
				rect2 = rects.get(j);
				if(isInTolerance(rect.x, rect2.x, tolerance) && isInTolerance(rect.y, rect2.y, tolerance)
						&& isInTolerance(rect.width, rect2.width, tolerance)
						&& isInTolerance(rect.height, rect2.height, tolerance)) {
					// System.out.println("find similar rect[" + i + "]" +
					// rect.toString() + ":" + "rect[" + j + "]" +
					// rect2.toString() + " ,reset!" );
					rects.get(j).height = 1;
					rects.get(j).width = 1;
				} else {
					// System.out.print("[" + j + "]");
				}
			}

			// remove unuseful rect
			if (rect.width < tolerance || rect.height < tolerance) {
				// System.out.println("\nremove little rect[" + i + "]" + ":" +
				// contoursRects.get(i).toString());
				rects.remove(i);
				i--;// after remove item, size() is updated so need i--
			}
		}
		return rects;
	}

	private ArrayList<PanelRect> findIrPanel(ArrayList<Rect> findrects) {
		int maxwidth = 0, maxheight = 0;
		ArrayList<PanelRect> panelrects = new ArrayList<>();
		summaryInfos = SummaryRectsInfo.summaryRect(findrects, SummaryType.SUMMARY_TYPE_WIDTH_HEIGHT);
		// sort
//		Collections.sort(summaryInfos, SummaryRectsInfo.heighComparator);
//		Collections.sort(summaryInfos, SummaryRectsInfo.widthComparator);

		SummaryRectsInfo inf = new SummaryRectsInfo(0, 0, 0, 0,SummaryType.SUMMARY_TYPE_WIDTH_HEIGHT);
		for (int i = 0; i < summaryInfos.size(); i++) {
			inf = summaryInfos.get(i);
			//System.out.println(inf.printinfo());
			if (imgSrcMat.width() / 2 < inf.getWidth() || imgSrcMat.height() / 2 > inf.getHeight()) {
				continue;
			}
			if (maxwidth / 2 > inf.getWidth() && maxheight / 2 > inf.getHeight()) { // speed
																					// up
				continue;
			}
			maxwidth = inf.getWidth();
			maxheight = inf.getHeight();
			for (int j = 0; j < inf.size(); j++) {
				ArrayList<Rect> childs = new ArrayList<>();
				Rect rect1 = inf.getRect(j);
				for (int j2 = 0; j2 < findrects.size(); j2++) {
					Rect rect2 = findrects.get(j2);
					if (rect1.contains(rect2.tl()) && rect1.contains(rect2.br())) {
						childs.add(rect2);
					}
				}
				if (childs.size() > 5) {// here
					PanelRect pRect = new PanelRect(rect1, childs);
					// System.out.println("PanelRect:" + pRect.toString());
					panelrects.add(pRect);
				}
			}
		}

		ArrayList<PanelRect> tmp = (ArrayList<PanelRect>) panelrects.clone();
		do {
			// System.out.println(" tmp size:" + tmp.size());
			for (int i = 0; i < tmp.size(); i++) {
				Rect rect1 = tmp.get(i).getRect();
				// System.out.println("#rect1:" + rect1.toString());
				for (int j = i + 1; j < tmp.size(); j++) {
					Rect rect2 = tmp.get(j).getRect();
					// System.out.println(" rect2:" + rect2.toString());
					if (rect1.contains(rect2.tl()) && rect1.contains(rect2.br())) {
						tmp.remove(j);
						j--;
						// System.out.println(" remove rect2" );
						continue;
					}
					if (rect2.contains(rect1.tl()) && rect2.contains(rect1.br())) {
						tmp.remove(i);
						i--;
						// System.out.println(" remove rect1" );
						continue;
					}
				}
			}
			if (tmp.size() == 1) {
				// System.out.println(" refind panelrects size
				// "+panelrects.size());
				panelrects.remove(tmp.get(0));
				tmp = (ArrayList<PanelRect>) panelrects.clone();
			} else if (tmp.size() == 0) {
				System.err.println("error, can not find panel");
				break;
			} else {
				break;
			}
		} while (true);

		return tmp;
	}

	private void drawPanelRect(){
		//reset img
		imgDstMat = imgSrcMat.clone();
		Image img = HighGui.toBufferedImage(imgDstMat);
		dstImageLable.setIcon(new ImageIcon(img));
		
		//draw panel rect
		Scalar color = new Scalar(30, 182, 255);
		Imgproc.rectangle(imgDstMat, irPanel1.getRect(), color, 2);
		Imgproc.rectangle(imgDstMat, irPanel2.getRect(), color, 2);
		
		//draw child rect
		color = new Scalar(0, 0, 255);
		for (Rect rect : irPanel1.getChilds()) {
			Imgproc.rectangle(imgDstMat, rect, color, 1);
		}
		for (Rect rect : irPanel2.getChilds()) {
			Imgproc.rectangle(imgDstMat, rect, color, 1);
		}
		img = HighGui.toBufferedImage(imgDstMat);
		dstImageLable.setIcon(new ImageIcon(img));
	}
	private void findIrPanel() {
		ArrayList<PanelRect> panelrects = new ArrayList<>();
		contoursRects = findContours(imgDstMat, findContoursThreshold);
		contoursRects = contoursFilter(contoursRects, filterContoursTolerance);
		panelrects = findIrPanel(contoursRects);
		if (panelrects.size() == 0) {
			return;
		}
		// for (int i = 0; i < contoursRects.size(); i++) {
		// Scalar color = new Scalar(0, 0, 255);
		// Imgproc.rectangle(imgDstMat, contoursRects.get(i).tl(),
		// contoursRects.get(i).br(), color, 1);
		// }
//		for (int i = 0; i < panelrects.size(); i++) {
//			System.out.println("PanelRect:" + panelrects.get(i).toString());
//			Scalar color = new Scalar(30, 182, 255);
//			Imgproc.rectangle(imgDstMat, panelrects.get(i).getRect().tl(), panelrects.get(i).getRect().br(), color, 2);
//		}
		if (panelrects.size() >= 3) {
			System.err.println("warning, get Ir Panel more than 2 !!!");
		}
		irPanel1 = panelrects.get(0);
		irPanel2 = panelrects.get(1);
		
		buttonFilter(irPanel1);
		irPanel1.childsSort();
		buttonFilter(irPanel2);
		irPanel2.childsSort();
		drawPanelRect();
		findButton();

	}

	private void rectContainFilter(ArrayList<Rect> rects){
		for (int i = 0; i < rects.size(); i++) {
			Rect rect1 = rects.get(i);
			// System.out.println("#rect1:" + rect1.toString());
			for (int j = i + 1; j < rects.size(); j++) {
				Rect rect2 = rects.get(j);
				// System.out.println(" rect2:" + rect2.toString());
				if (rect1.contains(rect2.tl()) && rect1.contains(rect2.br())) {
					rects.remove(j);
					j--;
					// System.out.println(" remove rect2" );
					continue;
				}
				if (rect2.contains(rect1.tl()) && rect2.contains(rect1.br())) {
					rects.remove(i);
					i--;
					// System.out.println(" remove rect1" );
					continue;
				}
			}
		}
	}
	
	private void buttonFilter(PanelRect panelrect){
		ArrayList<Rect> rects = panelrect.getChilds();
		System.out.println("buttonFilter " + panelrect.toString());
		Rect p = panelrect.getRect();
		for (int i = 0; i < rects.size(); i++) {
			Rect c = rects.get(i);
			if(c.width > p.width * 5/7 || c.width < p.width * 1/10) {
				rects.remove(i);
				i--;
				continue;
			}
			if(c.height > p.height /2 || c.height < p.height /50) {
				rects.remove(i);
				i--;
				continue;
			}
			
			if(c.x < p.x + 10 || c.y < p.y +10) {
				rects.remove(i);
				i--;
				continue;
			}
			
			if(c.x + c.width > p.x + p.width - 10  || c.y + c.height > p.y + p.height - 10) {
				rects.remove(i);
				i--;
				continue;
			}
		}
		rectContainFilter(panelrect.getChilds());
	}
	
	private Scalar RandomColor () {
        Random rng = new Random();
        rng.ints(80, 256);
        int r = rng.nextInt(256);
        int g = rng.nextInt(256);
        int b = rng.nextInt(256);
        return new Scalar(r, g, b);
    }
    
	private void findButton() {
		final File DESKPATH=new File("C:\\Users\\wang_wei\\Desktop\\test");
		if(!DESKPATH.exists()) {
			DESKPATH.mkdirs();
		}
		buttonFilter(irPanel1);
		irPanel1.childsSort();
		ArrayList<SummaryRectsInfo> sumInfos1 = SummaryRectsInfo.summaryRect(irPanel1.getChilds(), SummaryType.SUMMARY_TYPE_Y);
		System.out.println("-----panle1-------");
		for (SummaryRectsInfo summaryRectsInfo : sumInfos1) {
			System.out.println(summaryRectsInfo.printinfo(true));
			Scalar color = new Scalar(0, 0, 255);
			for (int i = 0; i < summaryRectsInfo.getChildCount(); i++) {
				Imgproc.rectangle(imgDstMat, summaryRectsInfo.getRect(i), color, 1);

				Imgcodecs.imwrite(DESKPATH + File.separator + "panel1_Y"+"_"+summaryRectsInfo.getY()+"_"+i+".png", new Mat(imgSrcMat, summaryRectsInfo.getRect(i)));
			}
		}

		buttonFilter(irPanel2);
		irPanel2.childsSort();
		ArrayList<SummaryRectsInfo> sumInfos2 = SummaryRectsInfo.summaryRect(irPanel2.getChilds(), SummaryType.SUMMARY_TYPE_Y);
		System.out.println("-----panle2-------");
		for (SummaryRectsInfo summaryRectsInfo : sumInfos2) {
			System.out.println(summaryRectsInfo.printinfo(true));
			Scalar color = new Scalar(0, 0, 255);
			for (int i = 0; i < summaryRectsInfo.getChildCount(); i++) {
				Imgproc.rectangle(imgDstMat, summaryRectsInfo.getRect(i), color, 1);
				Imgcodecs.imwrite(DESKPATH + File.separator + "panel2_Y"+"_"+summaryRectsInfo.getY()+"_"+i+".png", new Mat(imgSrcMat, summaryRectsInfo.getRect(i)));
			}
		}

//		ArrayList<SummaryRectsInfo> tmp1 = sumInfos2;
//		ArrayList<SummaryRectsInfo> tmp2 = sumInfos1;
//		if(sumInfos1.size() > sumInfos2.size()) {
//			tmp1 = sumInfos1;
//			tmp2 = sumInfos2;
//		}
//		for (SummaryRectsInfo info2 : tmp2) {
//			for (SummaryRectsInfo inf1 : tmp1) {
//				
//			}
//		}

	}

	private boolean isInTolerance(int frist, int second, int tolerance){
		if (Math.abs(frist - second) < tolerance) {
			return true;
		}
		return false;
	}

	private void calcGrayHist(Mat src){
        List<Mat> bgrPlanes = new ArrayList<>();
        Core.split(src, bgrPlanes);
        //! [Separate the image in 3 places ( B, G and R )]

        //! [Establish the number of bins]
        int histSize = 256;
        //! [Establish the number of bins]

        //! [Set the ranges ( for B,G,R) )]
        float[] range = {0, 256}; //the upper boundary is exclusive
        MatOfFloat histRange = new MatOfFloat(range);
        //! [Set the ranges ( for B,G,R) )]

        //! [Set histogram param]
        boolean accumulate = false;
        //! [Set histogram param]

        //! [Compute the histograms]
        Mat bHist = new Mat(), gHist = new Mat(), rHist = new Mat();
        Imgproc.calcHist(bgrPlanes, new MatOfInt(0), new Mat(), bHist, new MatOfInt(histSize), histRange, accumulate);
        Imgproc.calcHist(bgrPlanes, new MatOfInt(1), new Mat(), gHist, new MatOfInt(histSize), histRange, accumulate);
        Imgproc.calcHist(bgrPlanes, new MatOfInt(2), new Mat(), rHist, new MatOfInt(histSize), histRange, accumulate);
        //! [Compute the histograms]

        //! [Draw the histograms for B, G and R]
        int histW = 512, histH = 400;
        int binW = (int) Math.round((double) histW / histSize);

        Mat histImage = new Mat( histH, histW, CvType.CV_8UC3, new Scalar( 0,0,0) );
        //! [Draw the histograms for B, G and R]

        //! [Normalize the result to ( 0, histImage.rows )]
        Core.normalize(bHist, bHist, 0, histImage.rows(), Core.NORM_MINMAX);
        Core.normalize(gHist, gHist, 0, histImage.rows(), Core.NORM_MINMAX);
        Core.normalize(rHist, rHist, 0, histImage.rows(), Core.NORM_MINMAX);
        //! [Normalize the result to ( 0, histImage.rows )]

        //! [Draw for each channel]
        float[] bHistData = new float[(int) (bHist.total() * bHist.channels())];
        bHist.get(0, 0, bHistData);
        float[] gHistData = new float[(int) (gHist.total() * gHist.channels())];
        gHist.get(0, 0, gHistData);
        float[] rHistData = new float[(int) (rHist.total() * rHist.channels())];
        rHist.get(0, 0, rHistData);

        for( int i = 1; i < histSize; i++ ) {
            Imgproc.line(histImage, new Point(binW * (i - 1), histH - Math.round(bHistData[i - 1])),
                    new Point(binW * (i), histH - Math.round(bHistData[i])), new Scalar(255, 0, 0), 2);
            Imgproc.line(histImage, new Point(binW * (i - 1), histH - Math.round(gHistData[i - 1])),
                    new Point(binW * (i), histH - Math.round(gHistData[i])), new Scalar(0, 255, 0), 2);
            Imgproc.line(histImage, new Point(binW * (i - 1), histH - Math.round(rHistData[i - 1])),
                    new Point(binW * (i), histH - Math.round(rHistData[i])), new Scalar(0, 0, 255), 2);
        }
        
        //! [Draw for each channel]
        HighGui.imshow( "Source image", src );
        HighGui.imshow( "calcHist Demo", histImage );
        
        HighGui.waitKey(0);
        
        //System.exit(0);
	}
}